function print_table( t, print_func, level )
	local print_cache = nil;
	if print_func == nil then
		print_cache = {};
		print_func = function(...)
			if #print_cache > 64 then
				print( table.concat(print_cache,"\n") );
				print_cache = {};
			end;
			table.insert(print_cache, table.concat({...}, ""));
		end;
	end;
	if level == nil then
		level = '';
	end;
	for name, val in pairs(t) do
		if type(val) == "table" then
			print_func( level .. name .. ' : {' );
			print_table( val, print_func, level .. "\t" );
			print_func( level .. '};' );
		else
			local common_formator = function(v) return "{"..type(v).."}";end;
			local type_formattor = {
				["userdata"] = common_formator;
				["function"] = common_formator;
				["boolean"] = function(v) if v then return "true"; end; return "false"; end;
			};
			if type_formattor[type(val)] then
				val = type_formattor[type(val)](val);
			end;
			print_func( level .. name .. ' : ' .. val .. ';' );
		end;
	end;
	if print_cache then
		print(table.concat(print_cache, "\n"));
	end;
end;

lua_ex.print_class_info = function( self, class_name, print_func )
	local print_cache = nil;
	if print_func == nil then
		print_cache = {};
		print_func = function(...) 
			if #print_cache > 64 then
				print( table.concat(print_cache,"\n") );
				print_cache = {};
			end;
			table.insert(print_cache, table.concat({...}, ""));
		end;
	end;
	local function print_member( info_table, level )
		if level == nil then
			level = '';
		end;
		for key, val in pairs(info_table) do
			local ret_type = val[1];
			local name = val[2];
			local param = val["param"];
			local desc = val["desc"];
			if desc ~= nil then
				print_func( level .. "// " .. key .. " " .. desc );
			else
				print_func( level .. "// " .. key );
			end;
			local decl_text = ret_type .. " " .. name .. "(";
			if param ~= nil then
				local with_desc = false;
				local params = {};
				local pnum = 0;
				for pk, pv in pairs(param) do
					pnum = pnum + 1;
					local pinfo;
					if type(pv) == "table" then
						local ptype = pv[1];
						local pname = pv["name"];
						local desc = pv["desc"];
						pinfo = ptype;
						if pname then 
							pinfo = pinfo .. " " .. pname;
						end;
						if desc then
							pinfo = { pinfo, desc };
							with_desc = true;
						else
							pinfo = { pinfo };
						end;
					else
						pinfo = { pv };
					end;
					params[ pk ] = pinfo;
				end;
				if with_desc then
					print_func( level .. decl_text );
					local count = 0;
					for pk, pv in pairs(params) do
						count = count + 1;
						local text = pv[1];
						if count < pnum then
							text = text .. ", ";
						end;
						if pv[2] then
							text = text .. "\t// " .. pv[2];
						end;
						print_func( level .. "\t" .. text );
					end;
					print_func( level .. ");" );
				else
					local count = 0;
					for pk, pv in pairs(params) do
						count = count + 1;
						local text = pv[1];
						if count < pnum then
							text = text .. ", ";
						end;
						decl_text = decl_text .. text;
					end;
					print_func( level .. decl_text .. ");" );
				end;
			else
				print_func( level .. decl_text .. ");" );
			end;
		end;
	end;

	local info = self:class_info( class_name );
	if  type(info) ~= "table" then
		print_func( "Class info for [" .. class_name .. "] not found!" );
		return false;
	end;
	print_func( "class " .. info["name"] .. " {" );
	if type(info["member"]) == "table" and (#info["member"]) > 0 then
		print_func( "\t///////////////" );
		print_func( "\t// methods" );
		print_func( "\t///////////////" );
		print_member( info["member"], "\t" );
	end;
	if type(info["event"]) == "table" and (#info["event"]) > 0 then
		print_func( "\t///////////////" );
		print_func( "\t// events" );
		print_func( "\t///////////////" );
		print_member( info["event"], "\t" );
	end;
	print_func( "};" );

	if print_cache then
		print(table.concat(print_cache, "\n"));
	end;
	return true;
end

function enum( enumerator )
	return function() 
		if enumerator.eof() then
			enumerator.destroy();
			enumerator = nil;
			return nil;
		else
			local name = enumerator.name;
			local value = enumerator.value;
			enumerator.next();
			return name, value;
		end;
	end;
end;

function sizeof( obj )
	if type(obj) == "string" then
		return #obj;
	end;
	if type(obj) == "table" then
		local counter = 0;
		for idx, val in pairs(obj) do
			counter = counter+1;
		end;
		return counter;
	end;
	if obj == nil then
		return 0;
	end;
	error( "can't get size" );
end;

-- make relative path into absolute path
function abs_path(in_path)
	local path, n = string.gsub(in_path, [[([/\][^/\]+[/\]%.%.)]], "", 1);
	if n > 0 then
		return abs_path(path);
	end
	local path, m = string.gsub(path, [[([/\]+)]], "\\");
	return path;
end

-- concat all input parts into the file path
function make_path(...)
	local path_list = { ... };
	for idx, path in ipairs(path_list) do
		local last_ch = string.sub(path, #path);
		local first_ch = string.sub(path, 1);
		if last_ch == '\\' or last_ch == '/' then
			path = string.sub(path, 1, #path -1);
		end;
		if first_ch == '\\' or first_ch == '/' then
			path = string.sub(path, 2, #path);
		end;
		path_list[idx] = path;
	end;
	return abs_path(table.concat(path_list, "\\"));
end

-- extract file name from input path
function get_file_name(in_path)
	local path, n = string.gsub(in_path, [[^(%w:[/\])]], "");
	path, n = string.gsub(path, [[([^/\]+[/\])]], "");
	if n > 0 then
		return get_file_name(path);
	end
	return path;
end

-- extract file base name from input path
function get_base_name(in_path)
	local full_name = get_file_name(in_path);
	local basename, n = string.gsub(full_name, [[(%.[^.]+)$]], "");
	return basename;
end

-- extract file extra name from input path
function get_ext_name(in_path)
	local full_name = get_file_name(in_path);
	local basename, n = string.gsub(full_name, [[^(.+%.)]], "");
	return basename;
end

function find_first(lst, matcher)
	local m = matcher;
	if type(matcher) == "string" then
		local mlen = #matcher;
		m = function(v)
			return v:sub(0,mlen) == matcher;
		end;
	end;

	for i,v in ipairs(lst) do
		if m(v) then
			return v, i;
		end;
	end;
	return nil, nil;
end;

function find_reg_path( reg_path, matcher )
	local subkeys = utils:list_reg(reg_path);
	if subkeys == nil then
		return nil;
	end;
	local key = find_first(subkeys, matcher);
	if key == nil then
		return nil;
	end;
	local new_reg_path = make_path(reg_path, key);
	return new_reg_path;
end;

function find_reg_val( reg_path, matcher, val_path )
	local new_reg_path = find_reg_path(reg_path, matcher);
	if new_reg_path == nil then
		return nil;
	end;
	new_reg_path = make_path(new_reg_path, val_path);
	return utils:read_reg(new_reg_path);
end;

function split_string(str, sp)
	local match = function(i,ch,str) return sp==ch; end;
	if type(sp) == "function" then
		match = sp;
	end;
	local strs = {};
	local start = 1;
	for i=1,#str,1 do
		local ch = str:sub(i,i);
		if match(i,ch,str) then
			if i>start then
				table.insert(strs, str:sub(start,i-1));
			end;
			start = i+1;
		end;
	end;
	if start <= #str then
		table.insert(strs, str:sub(start));
	end;
	return table.unpack(strs);
end;

function load_file(file_path)
	local data_file = io.open(file_path, "r");
	local data = data_file:read("a");
	data_file:close();
	return data;
end;

function write_file(file_path, file_data)
	local data_file = io.open(file_path, "w+");
	data_file:write(file_data);
	data_file:close();
end;

function load_json(file_path)
	return utils:fromjson(load_file(file_path));
end;

function write_json(file_path, data)
	local json_data = utils:tojson(data);
	write_file(file_path, json_data);
end;

table.clone = function(...)
	local new_tab = {};
	for i, tab in ipairs({...}) do
		for name, value in pairs(tab) do
			new_tab[name] = value;
		end;
	end;
	return new_tab;
end;

table.iclone = function(tab, ...)
	local new_tab = {};
	local idx = 0;
	for i, tab in ipairs({...}) do
		for name, value in ipairs(tab) do
			idx = idx + 1;
			new_tab[idx] = value;
		end;
	end;
	return new_tab;
end;

if table.unpack == nil then
	-- fix luajit compatibility issue
	table.unpack = unpack;
end;

table.keys = function(tab)
	local keys = {};
	for key, val in pairs(tab) do
		table.insert(keys, key);
	end;
	return keys;
end;

table.values = function(tab)
	local values = {};
	for key, val in pairs(tab) do
		table.insert(values, val);
	end;
	return values;
end;

if not math.tointeger then
	math.tointeger = function( val )
		return math.floor(tonumber(val));
	end;
end;
